Pattern = class()

function Pattern:init(update)
    self.done = false
    self.update = update
end

-- takes function(size, iteration, edge, world) and returns function(edge) end end
-- if the function returns nil, end it all
function newPattern(fn)
    return function(edge)
        edge = edge or ({'top', 'bottom', 'left', 'right'})[math.random(1, 4)]

        return { edge = edge, pattern = Pattern(function(inst, world)
            inst.counter = inst.counter and inst.counter + 1 or 1

            local size = ((edge == 'left' or edge == 'right') and HEIGHT or WIDTH)

            local result = fn(size, inst.counter, edge, world)

            if not result then
                inst.done = true
                return
            end

            for _, p in pairs(result) do
                if p > 0 and p < size then
                    local bullet = ({
                        top = Bullet(p, 1, 0, 1),
                        bottom = Bullet(p, HEIGHT, 0, -1),
                        left = Bullet(1, p, 1, 0),
                        right = Bullet(WIDTH, p, -1, 0)
                    })[edge]

                    world.bullets[#world.bullets + 1] = bullet
                end
            end
        end)}
    end
end

EDGE_PATTERNS = {
    newPattern(function(size, iter, edge, world)
        if iter >= size then
            return nil
        elseif iter % 3 > 0 then
            return {}
        end

        local out = {}

        for i = math.min(iter, size), math.min(iter + 8, size), 2 do
            out[#out + 1] = i
        end

        return out
    end),
    newPattern(function(size, iter, edge, world)
        local pos = (edge == 'top' or edge == 'bottom') and world.player.x or world.player.y
        local out = {}

        if iter > 5 then
            return nil
        elseif iter == 5 or iter == 1 then
            for v = pos - 2, pos + 2 do
                out[#out + 1] = v
            end
        else
            out = {pos - 2, pos + 2}
        end

        return out
    end),
    newPattern(function(size, iter, edge, world)
        if iter > 7 then
            return nil
        end

        local pos = (edge == 'top' or edge == 'bottom') and world.player.x or world.player.y
        local out = {}

        out = {pos - iter + 1, pos + iter - 1}

        return out
    end),
    newPattern(function(size, iter, edge, world)
        if iter > 20 then
            return nil
        elseif iter > 6 then
            return {}
        elseif iter % 2 > 0 then
            return {}
        end

        local out = {}

        for i = 1, size do
            if math.floor((i + iter)/ 2) % 2 == 0 then
                out[#out + 1] = i
            end
        end

        return out
    end),
}
